/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "stdafx.h"

bool isWindowMinimized(HWND window, const Rect *screenRect)
{
	if(IsIconic(window))
		return true;
	else if(screenRect->left < -10000 || screenRect->top < -10000)
		return true;
	else
		return false;
}

/////////////////////////////////////////////////////////////////////////////

WindowData::WindowData(HWND handle)
{
	// Base properties
	this->handle = handle;
	this->pid = -1;
	GetWindowThreadProcessId(handle, &pid);
	
	// Cached properties
	RECT screenPos;
	if(!GetWindowRect(handle, &screenPos))
		screenPos = Rect(0,0,1,1);
	this->lastScreenPos = screenPos;
	this->minimized = isWindowMinimized(handle, &lastScreenPos);
	this->isTask = getIsTask();
	this->isPopup = (GetWindowLong(handle, GWL_STYLE) & WS_POPUP) ?true:false;
	this->parent = GetParent(handle);
	
	// Properties controlled by WindowTracker
	this->desk = NULL;
	this->task = NULL;
	this->flashing = false;
	
	// Flags
	this->tempSticky = false;
	this->visited = false;
	this->closeTime = 0;
	
	this->smallIcon = NULL;
	this->bigIcon = NULL;
	updateIcon();
}

WindowData::~WindowData()
{
	if(smallIcon)
		DestroyIcon(smallIcon);
	if(bigIcon)
		DestroyIcon(bigIcon);
}

Rect WindowData::getRect()
{
	RECT screenPos; GetWindowRect(handle, &screenPos);
	return Rect(screenPos);
}

bool WindowData::updateCachedProperties()
{
	bool changed = false;
	
	RECT screenPos; GetWindowRect(handle, &screenPos);
	updateAndFlag((Rect)screenPos, lastScreenPos, changed);
	
	updateAndFlag(isWindowMinimized(handle, &lastScreenPos), minimized, changed);
	
	updateAndFlag(getIsTask(), isTask, changed);
	bool newIsPopup = (GetWindowLong(handle, GWL_STYLE) & WS_POPUP) ?true:false;
	updateAndFlag(newIsPopup, isPopup, changed);
	updateAndFlag(GetParent(handle), parent, changed);
	
	return changed;
}

void WindowData::updateIcon()
{
	// While window icons may change and theoretically ought to be considered
	// cached properties, getting icons requires sending messages (potentially
	// lots of messages, as there's a series of fallbacks), which can block if
	// the app is busy, so we don't update. FIXME: This occasionally leads to
	// windows having the wrong icon on the taskbar.
	HICON newSmallIcon = getTaskIcon(handle, false, 500);
	HICON newBigIcon = getTaskIcon(handle, true, 500);
	
	if(newSmallIcon) {
		if(smallIcon) DestroyIcon(smallIcon);
		smallIcon = newSmallIcon;
	}
	if(newBigIcon) {
		if(bigIcon) DestroyIcon(bigIcon);
		bigIcon = newBigIcon;
	}
}

string WindowData::getTitle()
{
	char buf[1024];
	if(GetWindowTextA(handle, buf, 1024)) {
		lastTitle = buf;
		return string(buf);
	} else {
		return lastTitle;
	}
}

#define INVALID_ICON(icon) (!(icon) || (int)(icon)==-1 || (int)(icon)==1)

HICON WindowData::getTaskIcon(HWND window, bool preferBig, int timeout)
{
	HICON icon = NULL;
	
	if(preferBig)
	{
		icon = getTaskIconWithSize(window, true, timeout);
		if(!icon)
			icon = getTaskIconWithSize(window, false, timeout);
	}
	else
	{
		icon = getTaskIconWithSize(window, false, timeout);
		if(!icon)
			icon = getTaskIconWithSize(window, true, timeout);
	}
	
	// If no valid icon was found, use a default icon
	if(INVALID_ICON(icon))
	{
		icon = LoadIcon(NULL, IDI_APPLICATION);
		return icon;
	}
	else
	{
		HICON ret = CopyIcon(icon);
		return ret;
	}
}

HICON WindowData::getTaskIconWithSize(HWND window, bool big, int timeout)
{
	HICON ret;
	
	int messageIconSize = big ? ICON_BIG : ICON_SMALL;
	if(SendMessageTimeout(
		window,
		WM_GETICON,
		messageIconSize,
		0,
		SMTO_BLOCK|SMTO_ABORTIFHUNG,
		timeout,
		(PDWORD_PTR)&ret))
	{
		if(!INVALID_ICON(ret))
			return ret;
	}
	
	int classLongIconSize = big ? GCLP_HICON : GCLP_HICONSM;
	ret = (HICON)GetClassLongPtr(window, classLongIconSize);
	if(!INVALID_ICON(ret))
		return ret;

	if(SendMessageTimeout(
		window,
		WM_QUERYDRAGICON,
		messageIconSize,
		0,
		SMTO_BLOCK|SMTO_ABORTIFHUNG,
		timeout,
		(PDWORD_PTR)&ret))
	{
		if(!INVALID_ICON(ret))
			return ret;
	}
	
	return NULL;
}

HICON WindowData::getIcon(int size)
{
	if(size > 22)
		return bigIcon;
	else
		return smallIcon;
}

void WindowData::minimize()
{
	SendMessage(handle, WM_SYSCOMMAND, (WPARAM)SC_MINIMIZE, 0);
	minimized = true;
}

void WindowData::maximize()
{
	SendMessage(handle, WM_SYSCOMMAND, (WPARAM)SC_MAXIMIZE, 0);
}

void WindowData::restore()
{
	SendMessage(handle, WM_SYSCOMMAND, (WPARAM)SC_RESTORE, 0);
	minimized = false;
}

bool WindowData::isFocused()
{
	if(windowTracker->getForegroundWindow() == this)
		return true;
	if(minimized)
		return false;
	if(windowTracker->lastFocus == handle)
		return true;
	return false;
}

bool WindowData::isFocusedTask()
{
	return isFocused() || (windowTracker->getForegroundTask() == task);
}

bool WindowData::isMinimized()
{
	return minimized;
}

bool WindowData::getIsTask()
{
	// The definition of this function comes from
	// http://www.codeplex.com/shell/Wiki/View.aspx?title=Shell%20Events&referringTitle=Shell%20Startup
	// which got it from some uncited location in MSDN. It's fairly bizarre,
	// but must be reproduced exactly or things won't work right.
	
	if(!IsWindowVisible(handle))
		return false;
	
	HWND parent = GetParent(handle);
	if(parent!=0)
		return false;
	
	long exStyle = GetWindowLong(handle, GWL_EXSTYLE);
	HWND owner = GetWindow(handle, GW_OWNER);
	
	if(!(exStyle&WS_EX_TOOLWINDOW) && !owner)
		return true;
	else if((exStyle & WS_EX_APPWINDOW) && owner)
		return true;
	else
		return false;
}
